package com.shelpe.services.sustainer.autoDrive;

import java.util.Calendar;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang3.time.DateUtils;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.JobParametersInvalidException;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.core.configuration.annotation.StepBuilderFactory;
import org.springframework.batch.core.job.builder.SimpleJobBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.repository.JobRestartException;
import org.springframework.batch.core.step.builder.SimpleStepBuilder;
import org.springframework.batch.item.support.ListItemReader;
import org.springframework.batch.item.support.ListItemWriter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.scheduling.annotation.Scheduled;

import com.shelpe.services.sustainer.entity.jpa.Adgroup;
import com.shelpe.services.sustainer.repository.jpa.AdgroupRepository;
import com.shelpe.services.sustainer.repository.jpa.InprogressAutoDriverRepository;
import com.shelpe.services.sustainer.step.BlankEnderStep;
import com.shelpe.services.sustainer.step.BlankStarterStep;
import com.shelpe.services.sustainer.step.InprogressCleanerStep;
import com.shelpe.services.sustainer.step.InprogressRecordStep;

@Configuration
public class AutoDriveConfiguration {

	@Autowired
	private StepBuilderFactory steps;
	@Autowired
	private JobBuilderFactory jobs;
	@Autowired
	private InprogressAutoDriverRepository inprogressAutoDriverRepository;
	@Autowired
	private AdgroupRepository adgroupRepository;
	
	@Autowired
	private JobLauncher jobLauncher;
	@Autowired
	private BlankStarterStep starter;
	@Autowired
	private BlankEnderStep ender;
	@Autowired
	private InprogressJobs inprogressJobs;
	
	
	@Scheduled(cron = "*/30 * * * * ?")
	public void startAutoDrive() {
		try {
			JobParameters jobParams = new JobParametersBuilder().addLong("version", (new Date()).getTime()).toJobParameters();
			jobLauncher.run(this.startAutoDriveJob(), jobParams);
		} catch (JobExecutionAlreadyRunningException | JobRestartException | JobInstanceAlreadyCompleteException
				| JobParametersInvalidException e) {
			e.printStackTrace();
		}
	}
	
	@Scheduled(cron = "*/30 * * * * ?")
	public void startChooseWord() {
		try {
			JobParameters jobParams = new JobParametersBuilder().addLong("version", (new Date()).getTime()).toJobParameters();
			jobLauncher.run(this.chooseWordJob(), jobParams);
		} catch (JobExecutionAlreadyRunningException | JobRestartException | JobInstanceAlreadyCompleteException
				| JobParametersInvalidException e) {
			e.printStackTrace();
		}
	}
	
	@Scheduled(cron = "*/30 * * * * ?")
	public void qscoreSync() {
		try {
			JobParameters jobParams = new JobParametersBuilder().addLong("version", (new Date()).getTime()).toJobParameters();
			jobLauncher.run(this.qscoreSyncJob(), jobParams);
		} catch (JobExecutionAlreadyRunningException | JobRestartException | JobInstanceAlreadyCompleteException
				| JobParametersInvalidException e) {
			e.printStackTrace();
		}
	}
	
	@Scheduled(cron = "*/30 * * * * ?")
	public void immediateExecute() {
		try {
			JobParameters jobParams = new JobParametersBuilder().addLong("version", (new Date()).getTime()).toJobParameters();
			jobLauncher.run(this.immediateExecuteJob(), jobParams);
		} catch (JobExecutionAlreadyRunningException | JobRestartException | JobInstanceAlreadyCompleteException
				| JobParametersInvalidException e) {
			e.printStackTrace();
		}
	}
	
	@Scheduled(cron = "0 0 0/2 * * ?")
	public void adjustAdgroup() {
		try {
			JobParameters jobParams = new JobParametersBuilder().addLong("version", (new Date()).getTime()).toJobParameters();
			jobLauncher.run(this.adjustAdgroupJob(), jobParams);
		} catch (JobExecutionAlreadyRunningException | JobRestartException | JobInstanceAlreadyCompleteException
				| JobParametersInvalidException e) {
			e.printStackTrace();
		}
	}
	
	@Scheduled(cron = "0 0 0/2 * * ?")
	public void executeMobileQscore() {
		try {
			JobParameters jobParams = new JobParametersBuilder().addLong("version", (new Date()).getTime()).toJobParameters();
			jobLauncher.run(this.executeMobileQscoreJob(), jobParams);
		} catch (JobExecutionAlreadyRunningException | JobRestartException | JobInstanceAlreadyCompleteException
				| JobParametersInvalidException e) {
			e.printStackTrace();
		}
	}
	
	
	@Bean
	public InprogressRegisterStep autoDriverRegisterStep(JobRepository jobRepository) {
		InprogressRegisterStep step = new InprogressRegisterStep();
		step.setJobRepository(jobRepository);
		return step;
	}
	
	@Bean
	public InprogressUnregisterStep autoDriverUnregisterStep(JobRepository jobRepository) {
		InprogressUnregisterStep step = new InprogressUnregisterStep();
		step.setJobRepository(jobRepository);
		return step;
	}

	@Autowired
	private InprogressRegisterStep autoDriverRegisterStep;
	@Autowired
	private InprogressUnregisterStep autoDriverUnregisterStep;
	
	@Autowired
	private AutoProductProcessor autoProductProcessor;
	@Bean(name="startAutoDriveJob")
	@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
	public Job startAutoDriveJob() {
		SimpleJobBuilder builder = this.jobs.get("startAutoDriveJob").start(autoDriverRegisterStep);
		builder.next(this.startAutoDriveStep()).next(autoDriverUnregisterStep);
		return builder.build();
	}
	@Bean(name = "startAutoDriveStep")
	@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
	public Step startAutoDriveStep() {
		SimpleStepBuilder<AutoProductQueueInput, AutoProductQueueResult> builder = this.steps.get("startAutoDriveStep").chunk(5);
		builder.reader(this.toStartDriverItemReader()).processor(autoProductProcessor).writer(this.toStartDriverItemWriter());
		builder.faultTolerant().retry(Exception.class).retryLimit(10);
		return builder.build();
	}
	public AutoProductWriter toStartDriverItemWriter(){
		AutoProductWriter writer =  new AutoProductWriter();
		writer.setStep(1);
		writer.setInprogressAutoDriverRepository(this.inprogressAutoDriverRepository);
		return writer;
	}
	public InprogressDriverReader toStartDriverItemReader(){
		InprogressDriverReader reader = new InprogressDriverReader("startAutoDriveJob", 1);
		reader.setInprogressAutoDriverRepository(this.inprogressAutoDriverRepository);
		reader.setInprogressJobs(this.inprogressJobs);
		return reader;
	}
	
	
	@Autowired
	private WordChooseProcessor processor;
	@Bean(name="chooseWordJob")
	@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
	public Job chooseWordJob() {
		SimpleJobBuilder builder = this.jobs.get("startChooseWordJob").start(autoDriverRegisterStep);
		builder.next(this.startChooseWordStep()).next(autoDriverUnregisterStep);
		return builder.build();
	}
	@Bean(name = "startChooseWordStep")
	@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
	public Step startChooseWordStep() {
		SimpleStepBuilder<AutoProductQueueInput, AutoProductQueueResult> builder = this.steps.get("startChooseWordStep").chunk(5);
		builder.reader(this.toChooseDriverItemReader()).processor(processor).writer(this.toChooseDriverItemWriter());
		builder.faultTolerant().retry(Exception.class).retryLimit(10);
		return builder.build();
	}
	public AutoProductWriter toChooseDriverItemWriter(){
		AutoProductWriter writer =  new AutoProductWriter();
		writer.setStep(2);
		writer.setInprogressAutoDriverRepository(this.inprogressAutoDriverRepository);
		return writer;
	}
	public InprogressDriverReader toChooseDriverItemReader(){
		InprogressDriverReader reader = new InprogressDriverReader("chooseWordJob", 2);
		reader.setInprogressAutoDriverRepository(this.inprogressAutoDriverRepository);
		reader.setInprogressJobs(this.inprogressJobs);
		return reader;
	}
	
	@Autowired
	private MobileQscoreSyncProcessor mobileQscoreSyncProcessor;
	@Bean(name="qscoreSyncJob")
	@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
	public Job qscoreSyncJob() {
		SimpleJobBuilder builder = this.jobs.get("qscoreSyncJob").start(autoDriverRegisterStep);
		builder.next(this.qscoreSyncStep()).next(autoDriverUnregisterStep);
		return builder.build();
	}
	@Bean(name = "qscoreSyncStep")
	@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
	public Step qscoreSyncStep() {
		SimpleStepBuilder<AutoProductQueueInput, AutoProductQueueResult> builder = this.steps.get("qscoreSyncStep").chunk(5);
		builder.reader(this.toQscoreSyncItemReader()).processor(mobileQscoreSyncProcessor).writer(this.toQscoreSyncWriter());
		builder.faultTolerant().retry(Exception.class).retryLimit(10);
		return builder.build();
	}
	public AutoProductWriter toQscoreSyncWriter(){
		AutoProductWriter writer =  new AutoProductWriter();
		writer.setStep(3);
		writer.setInprogressAutoDriverRepository(this.inprogressAutoDriverRepository);
		return writer;
	}
	public InprogressDriverReader toQscoreSyncItemReader(){
		InprogressDriverReader reader = new InprogressDriverReader("qscoreSyncJob", 3);
		reader.setInprogressAutoDriverRepository(this.inprogressAutoDriverRepository);
		reader.setInprogressJobs(this.inprogressJobs);
		return reader;
	}
	
	
	@Autowired
	private ImmediateExecuteProcessor immediateExecuteProcessor;
	@Bean(name="immediateExecuteJob")
	@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
	public Job immediateExecuteJob() {
		SimpleJobBuilder builder = this.jobs.get("immediateExecuteJob").start(autoDriverRegisterStep);
		builder.next(this.immediateExecuteStep()).next(autoDriverUnregisterStep);
		return builder.build();
	}
	@Bean(name = "immediateExecuteStep")
	@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
	public Step immediateExecuteStep() {
		SimpleStepBuilder<AutoProductQueueInput, AutoProductQueueResult> builder = this.steps.get("immediateExecuteStep").chunk(5);
		builder.reader(this.toImmediateExecuteReader()).processor(immediateExecuteProcessor).writer(this.toImmediateExecuteWriter());
		builder.faultTolerant().retry(Exception.class).retryLimit(10);
		return builder.build();
	}
	public AutoProductWriter toImmediateExecuteWriter(){
		AutoProductWriter writer =  new AutoProductWriter();
		writer.setStep(4);
		writer.setInprogressAutoDriverRepository(this.inprogressAutoDriverRepository);
		return writer;
	}
	public InprogressDriverReader toImmediateExecuteReader(){
		InprogressDriverReader reader = new InprogressDriverReader("immediateExecuteJob", 4);
		reader.setInprogressAutoDriverRepository(this.inprogressAutoDriverRepository);
		reader.setInprogressJobs(this.inprogressJobs);
		return reader;
	}
	
	
	@Autowired
	private InprogressRecordStep inprogressRecorder;
	@Autowired
	private InprogressCleanerStep inprogressCleaner;
	
	@Autowired
	private AdjustAdgroupProcessor adjustAdgroupProcessor;
	@Bean(name="adjustAdgroupJob")
	@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
	public Job adjustAdgroupJob() {
		SimpleJobBuilder builder = this.jobs.get("adjustAdgroupJob").start(starter);
		builder.next(inprogressRecorder).next(this.adjustAdgroupStep()).next(inprogressCleaner).next(ender);
		return builder.build();
	}
	@Bean(name = "adjustAdgroupStep")
	@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
	public Step adjustAdgroupStep() {
		SimpleStepBuilder<Adgroup, AdjustAdgroupResult> builder = this.steps.get("adjustAdgroupStep").chunk(5);
		builder.reader(adjustAdgroupItemReader()).processor(adjustAdgroupProcessor).writer(adjustAdgroupItemWriter());
		builder.faultTolerant().retry(Exception.class).retryLimit(10);
		return builder.build();
	}
	public ListItemReader<Adgroup> adjustAdgroupItemReader(){
		Date today = DateUtils.truncate(new Date(), Calendar.DATE);
		Date ago3 = DateUtils.addDays(today, -3);
		List<Adgroup> adgroups = this.adgroupRepository.findByDriveTypeAndOnlineStatusAndMaintainedDateBetween(1, "online", ago3, ago3);
		List<Adgroup> newAdgroups = this.adgroupRepository.findByDriveTypeAndOnlineStatusAndMaintainedDateIsNull(1, "online");
		adgroups.addAll(newAdgroups);
		return new ListItemReader<Adgroup>(adgroups);
	}
	public ListItemWriter<AdjustAdgroupResult> adjustAdgroupItemWriter(){
		return new ListItemWriter<AdjustAdgroupResult>();
	}
	
	@Autowired
	private ExecuteMobileQscoreProcessor executeMobileQscoreProcessor;
	@Bean(name="executeMobileQscoreJob")
	@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
	public Job executeMobileQscoreJob() {
		SimpleJobBuilder builder = this.jobs.get("executeMobileQscoreJob").start(starter);
		builder.next(inprogressRecorder).next(this.executeMobileQscoreStep()).next(inprogressCleaner).next(ender);
		return builder.build();
	}
	@Bean(name = "executeMobileQscoreStep")
	@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
	public Step executeMobileQscoreStep() {
		SimpleStepBuilder<Adgroup, AdjustAdgroupResult> builder = this.steps.get("executeMobileQscoreStep").chunk(5);
		builder.reader(executeMobileQscoreItemReader()).processor(executeMobileQscoreProcessor).writer(this.executeMobileQscoreItemWriter());
		builder.faultTolerant().retry(Exception.class).retryLimit(10);
		return builder.build();
	}
	public ListItemReader<Adgroup> executeMobileQscoreItemReader(){
		Date today = DateUtils.truncate(new Date(), Calendar.DATE);
		Date yesterday = DateUtils.addDays(today, -1);
		List<Adgroup> adgroups = this.adgroupRepository.findByDriveTypeAndOnlineStatusAndMaintainedDateBetween(2, "online", yesterday, yesterday);
		List<Adgroup> newAdgroups = this.adgroupRepository.findByDriveTypeAndOnlineStatusAndMaintainedDateIsNull(2, "online");
		adgroups.addAll(newAdgroups);
		return new ListItemReader<Adgroup>(adgroups);
	}
	public ListItemWriter<AdjustAdgroupResult> executeMobileQscoreItemWriter(){
		return new ListItemWriter<AdjustAdgroupResult>();
	}
}
